﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace SuperBall3D
{
    public abstract class SuperBallObject : DrawableGameComponent
    {
        private Vector3 position;
        protected Model superBallModel;
        protected Texture2D modelTexture;
        protected Game thisGame;
        protected Matrix RotationMatrix = Matrix.Identity;
        Geometry geo;
        float radiusScale;
        float xScale;
        float yScale;
        float zScale;
        bool centerOnBottom;
        float frictionCoefficient;
        float elasticity;

        public SuperBallObject(Game g, bool centerOnBot, float friction, float elas)
            : base(g)
        {
            
            thisGame = g;
            centerOnBottom = centerOnBot;
            frictionCoefficient = friction;
            elasticity = elas;
            allSuperBallObjects.Add(this);
        }

        public abstract string GenerateConstructor();

        private static List<SuperBallObject> allSuperBallObjects = new List<SuperBallObject>();

        protected static List<SuperBallObject> dynamicSuperBallObjects = new List<SuperBallObject>();

        public static List<Platform> platformList = new List<Platform>();

        protected override void LoadContent()
        {
            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        // Here is the source of our woes:
        public override void Draw(GameTime gameTime)
        {
            Matrix[] transforms = new Matrix[superBallModel.Bones.Count];
            superBallModel.CopyAbsoluteBoneTransformsTo(transforms);
            Matrix scale = Matrix.Identity;

            foreach (ModelMesh mesh in superBallModel.Meshes)
            {
                
                foreach (BasicEffect effect in mesh.Effects)
                {
                    if(geo.GeomType == Geometry.GeometryType.Box)
                        scale = Matrix.CreateScale(XScale, YScale, ZScale);
                    else if (geo.GeomType == Geometry.GeometryType.Sphere)
                        scale = Matrix.CreateScale(RadiusScale);

                    
                    effect.Projection = Camera.ProjectionMatrix;
                    effect.View = Camera.ViewMatrix;
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index] * scale * RotationMatrix * Matrix.CreateTranslation(Position);
                }
                mesh.Draw();
            }
            
            
            base.Draw(gameTime);
        }
        
        
        static public List<SuperBallObject> AllSuperBallObjects
        {
            get { return allSuperBallObjects; }
        }


        static public List<SuperBallObject> DynamicSuperBallObjects
        {
            get { return dynamicSuperBallObjects; }
        }

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        public Geometry Geo
        {
            get { return geo; }
            protected set { geo = value; }
        }

        public float RadiusScale
        {
            get { return radiusScale; }
            protected set { radiusScale = value; }
        }

        public float XScale
        {
            get { return xScale; }
            set { xScale = value; }
        }

        public float YScale
        {
            get { return yScale; }
            set { yScale = value; }
        }

        public float ZScale
        {
            get { return zScale; }
            set { zScale = value; }
        }

        public bool CenterOnBottom
        {
            get { return centerOnBottom; }
        }

        public float FrictionCoefficient
        {
            get { return frictionCoefficient; }
        }

        public float Elasticity
        {
            get { return elasticity; }
        }

        public Vector3 CenterOffset
        {
            get {
                if (CenterOnBottom)
                    return ((getModelHeight(superBallModel) / 2) * Vector3.Up);
                else
                    return Vector3.Zero;
                }
        }


        float getModelHeight(Model m)
        {
            float min = float.MaxValue;
            float max = float.MinValue;

            Matrix[] bones = new Matrix[m.Bones.Count];
            m.CopyAbsoluteBoneTransformsTo(bones);
             

            foreach (ModelMesh mesh in m.Meshes)
            {
                //get the transform of the current mesh
                Matrix transform = bones[mesh.ParentBone.Index];

                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    //get the current mesh info
                    int stride = part.VertexBuffer.VertexDeclaration.VertexStride;
                    int vertexBufferSize = part.NumVertices * stride;
                    float[] verticesData = new float[vertexBufferSize / sizeof(float)];
                    part.VertexBuffer.GetData<float>(verticesData);

                    for (int i = 0; i < vertexBufferSize / sizeof(float); i += stride / sizeof(float))
                    {
                        Vector3 transformedPosition = Vector3.Transform(new Vector3(verticesData[i], verticesData[i + 1], verticesData[i + 2]), transform);

                        min = Math.Min(min, transformedPosition.Y);
                        max = Math.Max(max, transformedPosition.Y);


                    }
                }
            }

            return max - min;
        }

        public virtual void HandleCollisionWithStatic(Vector3 collisionNormal, float penetrationDepth, float otherObjFriction)
        { 
        }
        public virtual void HandleCollisionWithDynamic(DynamicSuperBallObject obj, Vector3 thisVelocity, Vector3 otherVelocity, Vector3 collisionNormal, float penetrationDepth)
        {
        }

        public static void RemoveFromGameLists(SuperBallObject obj)
        {
            if (obj is DynamicSuperBallObject)
                dynamicSuperBallObjects.Remove(obj);
            allSuperBallObjects.Remove(obj);
        }

        }

}
